package top.goldenyear.porpoise.common.core.model;

import cn.hutool.v7.core.text.StrUtil;
import org.noear.snack.ONode;
import top.goldenyear.porpoise.common.core.util.convert.ConvertUtils;

import java.io.Serial;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * 用 KeyValue 就是 Kv 的别名实现，但key只支持String，避免都需要引入 Enjoy。
 *
 * @author airhead
 */
@SuppressWarnings("unchecked")
public class KeyValue extends HashMap<String, Object> {

  @Serial
  private static final long serialVersionUID = -808251639784763326L;

  public KeyValue() {
  }

  public static KeyValue of() {
    return new KeyValue();
  }

  public static KeyValue of(Map<String, ?> map) {
    return new KeyValue().set(map);
  }

  public static KeyValue of(String key, Object value) {
    return new KeyValue().set(key, value);
  }

  public KeyValue set(String key, Object value) {
    super.put(key, value);
    return this;
  }

  public KeyValue setIfNotBlank(String key, String value) {
    if (StrUtil.isNotBlank(value)) {
      set(key, value);
    }
    return this;
  }

  public KeyValue setIfNotNull(String key, Object value) {
    if (value != null) {
      set(key, value);
    }
    return this;
  }

  public KeyValue set(Map<String, ?> map) {
    super.putAll(map);
    return this;
  }

  public KeyValue set(KeyValue kv) {
    super.putAll(kv);
    return this;
  }

  public KeyValue delete(String key) {
    super.remove(key);
    return this;
  }

  public <T> T getAs(String key) {
    return (T) get(key);
  }

  public <T> T getAs(String key, T defaultValue) {
    Object ret = get(key);
    return ret != null ? (T) ret : defaultValue;
  }

  public <T> T getAs(String key, Function<Object, T> converter) {
    Object ret = get(key);
    return ret != null ? converter.apply(ret) : null;
  }

  public <T> T getAs(String key, T defaultValue, Function<Object, T> converter) {
    Object ret = get(key);
    return ret != null ? converter.apply(ret) : defaultValue;
  }

  public String getStr(String key) {
    Object s = get(key);
    return s != null ? s.toString() : null;
  }

  public Integer getInt(String key) {
    return ConvertUtils.toInt(get(key));
  }

  public Long getLong(String key) {
    return ConvertUtils.toLong(get(key));
  }

  public BigDecimal getBigDecimal(Object key) {
    return ConvertUtils.toBigDecimal(get(key));
  }

  public Double getDouble(String key) {
    return ConvertUtils.toDouble(get(key));
  }

  public Float getFloat(String key) {
    return ConvertUtils.toFloat(get(key));
  }

  public Number getNumber(String key) {
    return ConvertUtils.toNumber(get(key));
  }

  public Boolean getBoolean(String key) {
    return ConvertUtils.toBoolean(get(key));
  }

  public Date getDate(String key) {
    return ConvertUtils.toDate(get(key));
  }

  public LocalDateTime getLocalDateTime(String key) {
    return ConvertUtils.toLocalDateTime(get(key));
  }

  public String getStr(String key, String defaultValue) {
    Object s = get(key);
    return s != null ? s.toString() : defaultValue;
  }

  public Integer getInt(String key, Integer defaultValue) {
    Object value = get(key);
    return value != null ? ConvertUtils.toInt(value) : defaultValue;
  }

  public Long getLong(String key, Long defaultValue) {
    Object value = get(key);
    return value != null ? ConvertUtils.toLong(value) : defaultValue;
  }

  public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) {
    Object value = get(key);
    return value != null ? ConvertUtils.toBigDecimal(value) : defaultValue;
  }

  public Double getDouble(String key, Double defaultValue) {
    Object value = get(key);
    return value != null ? ConvertUtils.toDouble(value) : defaultValue;
  }

  public Float getFloat(String key, Float defaultValue) {
    Object value = get(key);
    return value != null ? ConvertUtils.toFloat(value) : defaultValue;
  }

  public Number getNumber(String key, Number defaultValue) {
    Object value = get(key);
    return value != null ? ConvertUtils.toNumber(value) : defaultValue;
  }

  public Boolean getBoolean(String key, Boolean defaultValue) {
    Object value = get(key);
    return value != null ? ConvertUtils.toBoolean(value) : defaultValue;
  }

  public Date getDate(String key, Date defaultValue) {
    Object value = get(key);
    return value != null ? ConvertUtils.toDate(value) : defaultValue;
  }

  public LocalDateTime getLocalDateTime(String key, LocalDateTime defaultValue) {
    Object value = get(key);
    return value != null ? ConvertUtils.toLocalDateTime(value) : defaultValue;
  }

  /**
   * key 存在，并且 value 不为 null
   */
  public boolean notNull(String key) {
    return get(key) != null;
  }

  /**
   * key 不存在，或者 key 存在但 value 为null
   */
  public boolean isNull(String key) {
    return get(key) == null;
  }

  /**
   * key 所对应的 value 值不为空白字符串
   */
  public boolean notBlank(String key) {
    return StrUtil.isNotBlank(getStr(key));
  }

  /**
   * key 所对应的 value 值为空白字符串
   */
  public boolean isBlank(String key) {
    return StrUtil.isBlank(getStr(key));
  }

  /**
   * key 存在，并且 value 为 true，则返回 true
   */
  public boolean isTrue(String key) {
    Object value = get(key);
    return value != null && ConvertUtils.toBoolean(value);
  }

  /**
   * key 存在，并且 value 为 false，则返回 true
   */
  public boolean isFalse(String key) {
    Object value = get(key);
    return value != null && !ConvertUtils.toBoolean(value);
  }

  public String toJson() {
    return ONode.stringify(this);
  }

  @Override
  public boolean equals(Object kv) {
    return kv instanceof KeyValue && super.equals(kv);
  }

  public KeyValue keep(String... keys) {
    if (keys != null && keys.length > 0) {
      KeyValue newKv = KeyValue.of();
      for (String k : keys) {
        // 避免将并不存在的变量存为 null
        if (containsKey(k)) {
          newKv.put(k, get(k));
        }
      }

      clear();
      putAll(newKv);
    } else {
      clear();
    }

    return this;
  }

  public <V> Map<String, V> toMap() {
    return (Map<String, V>) this;
  }
}
